$(document).ready(function () {

    // 渲染左右分割栏
    renderSplitter();

    // 渲染报告图表
    renderEcharts();

    // 渲染报告节点树
    renderReportTree();

    // 注册事件
    $btn_report_log.on('click', showReportLogModal);

    // 渲染报告日志文本编辑器
    renderReportLogAceEditor();

});

let reportTree = null;
let filterResultFlag = '';  // 重复过滤时恢复缺省（清除过滤）
let ace_report_log_editor = null;  // 报告日志文本编辑器
let request_header_default_data_arr = {};  // key为dispatcher_detail_id, value为请求头默认数据
let request_body_default_data_arr = {};    // key为dispatcher_detail_id, value为请求体默认数据
let response_header_default_data_arr = {};    // key为dispatcher_detail_id, value为请求体默认数据
let response_body_default_data_arr = {};    // key为dispatcher_detail_id, value为请求体默认数据
let rendered_node_arr = [];  // 记录已经被渲染的节点（防止重复渲染）

// 界面元素
let $input_dispatcher_id = $('#input-dispatcher-id');
let $btn_report_log = $('#btn-report-log');
let $report_detail_data = $('#report-detail-data');
let $report_dispatcher_status = $('#report-dispatcher-status');
let $report_dispatcher_spinner = $('#report-dispatcher-spinner');
let $report_case_total_count = $('#report-case-total-count');
let $report_case_success_count = $('#report-case-success-count');
let $report_case_failure_count = $('#report-case-failure-count');
let $report_case_error_count = $('#report-case-error-count');
let $report_case_skip_count = $('#report-case-skip-count');
let $modal_report_log = $('#modal-report-log');
let $modal_report_log_dispatcher_status = $('#modal-report-log-dispatcher-status');
let $modal_report_log_dispatcher_spinner = $('#modal-report-log-dispatcher-spinner');
let $div_report_detail_data_load_spinner = $('#report-detail-data-load-spinner');


// 支持页面左右分割
function renderSplitter() {
    Split(['#report-tree-navigation', '#report-detail-data'], {
        // https://github.com/nathancahill/split/tree/master/packages/splitjs
        sizes: [25, 75],
        minSize: [200, 500],
        expandToMin: true,
        gutterSize: 10,
        direction: 'horizontal',
        cursor: 'col-resize',
        gutter: function (index, direction, pairElement) {
            const gutter = document.createElement('div');
            gutter.className = `gutter gutter-${direction} div-split-gutter-vertical`;
            return gutter
        }
        // direction: 'vertical',
    });
}

// 渲染报告日志文本编辑器
function renderReportLogAceEditor() {
    // 渲染Editor
    ace_report_log_editor = ace.edit('div-ace-report-log');
    ace_report_log_editor.setReadOnly(true);
    ace_report_log_editor.setTheme("ace/theme/log");
    ace_report_log_editor.session.setMode("ace/mode/log");

    // 定时查询直到调度执行结束
    let dispatcher_id = $input_dispatcher_id.val();
    let rowNum = 0;
    let _iIntervalID = setInterval(function() {
        rowNum = ace_report_log_editor.session.getLength();  // 下次日志更新插入位置行数
        $.ajax({
            type: 'POST',
            url: '/ajax/report_detail/log/get',
            data: {
                row_num: rowNum,
                dispatcher_id: dispatcher_id,
            },
            success: function (data, status, xhr) {
                if (data.error_no === 0){
                    if(data.status === '已完成' || data.status === '已停止'){
                        clearInterval(_iIntervalID);
                        $modal_report_log_dispatcher_spinner.css('display', 'none');
                        $report_dispatcher_spinner.css('display', 'none');
                    }
                    $modal_report_log_dispatcher_status.text(data.status);
                    $report_dispatcher_status.text(data.status);
                    ace_report_log_editor.session.insert({row:rowNum, column:0}, data.content);
                    // 当滚动条滚动到最底部时，插入日志都会进行自动滚动
                    if (ace_report_log_editor.renderer.getScrollBottomRow() >= rowNum - 1){
                        ace_report_log_editor.renderer.scrollToLine(999999);
                    }
                }else{
                    message("日志查询失败: " + data.error_msg, "error");
                }
            },
        });
    }, 3000);
}

// 加载项目页面和数据
function renderProjectReportDetail(node){
    return new Promise((resolve, reject) => {
        $.ajax({
            type: 'POST',
            url: '/ajax/report_detail/project',
            data: {
                dispatcher_detail_id: node.data.dispatcher_detail_id,
            },
            success: function (data, status, xhr) {
                if (data.error_no === 0) {
                    $report_detail_data.append(data.html_text);
                    resolve(['成功了', 'success']);  // 也可以传递其他类型参数
                } else {
                    // 渲染失败从已渲染数组中删除
                    rendered_node_arr.splice($.inArray(node.data.dispatcher_detail_id, rendered_node_arr), 1);
                    message("项目信息查询失败: " + data.error_msg, "error");
                }
            },
        })
    });
}
// 加载模块页面和数据
function renderModuleReportDetail(node){
    return new Promise((resolve, reject) => {
        $.ajax({
            type: 'POST',
            url: '/ajax/report_detail/module',
            data: {
                dispatcher_detail_id: node.data.dispatcher_detail_id,
            },
            success: function (data, status, xhr) {
                if (data.error_no === 0){
                    $report_detail_data.append(data.html_text);
                    resolve(['成功了', 'success']);  // 也可以传递其他类型参数
                }else{
                    // 渲染失败从已渲染数组中删除
                    rendered_node_arr.splice($.inArray(node.data.dispatcher_detail_id, rendered_node_arr), 1);
                    message("模块信息查询失败: " + data.error_msg, "error");
                }
            },
        })
    });
}
// 加载场景页面和数据
function renderSceneReportDetail(node) {
    return new Promise((resolve, reject) => {
        $.ajax({
            type: 'POST',
            url: '/ajax/report_detail/scene',
            data: {
                dispatcher_detail_id: node.data.dispatcher_detail_id,
            },
            success: function (data, status, xhr) {
                if (data.error_no === 0) {
                    $report_detail_data.append(data.html_text);
                    resolve(['成功了', 'success']);  // 也可以传递其他类型参数
                } else {
                    // 渲染失败从已渲染数组中删除
                    rendered_node_arr.splice($.inArray(node.data.dispatcher_detail_id, rendered_node_arr), 1);
                    message("场景信息查询失败: " + data.error_msg, "error");
                }
            },
        })
    });
}
// 加载逻辑控制器页面和数据
function renderLogicControllerReportDetail(node) {
    return new Promise((resolve, reject) => {
        $.ajax({
            type: 'POST',
            url: '/ajax/report_detail/logic_controller',
            data: {
                dispatcher_detail_id: node.data.dispatcher_detail_id,
            },
            success: function (data, status, xhr) {
                if (data.error_no === 0) {
                    $report_detail_data.append(data.html_text);
                    resolve(['成功了', 'success']);  // 也可以传递其他类型参数
                } else {
                    // 渲染失败从已渲染数组中删除
                    rendered_node_arr.splice($.inArray(node.data.dispatcher_detail_id, rendered_node_arr), 1);
                    message("逻辑控制器信息查询失败: " + data.error_msg, "error");
                }
            },
        })
    });
}
// 加载案例页面和数据
function renderCaseReportDetail(node) {
    return new Promise((resolve, reject) => {
        $.ajax({
            type: 'POST',
            url: '/ajax/report_detail/case',
            data: {
                dispatcher_detail_id: node.data.dispatcher_detail_id,
            },
            success: function (data, status, xhr) {
                if (data.error_no === 0) {
                    $report_detail_data.append(data.html_text);
                    // 渲染案例页面组件
                    renderCasePage(node.data.dispatcher_detail_id, data.expectations);
                    resolve(['成功了', 'success']);  // 也可以传递其他类型参数
                } else {
                    // 渲染失败从已渲染数组中删除
                    rendered_node_arr.splice($.inArray(node.data.dispatcher_detail_id, rendered_node_arr), 1);
                    message("案例信息查询失败: " + data.error_msg, "error");
                }
            },
        })
    });
}
// 加载工具页面和数据
function renderToolReportDetail(node){
    return new Promise((resolve, reject) => {
        $.ajax({
            type: 'POST',
            url: '/ajax/report_detail/tool',
            data: {
                dispatcher_detail_id: node.data.dispatcher_detail_id,
            },
            success: function (data, status, xhr) {
                if (data.error_no === 0){
                    $report_detail_data.append(data.html_text);
                    if(data.tool_type === 'SCRIPT'){
                        // 渲染ScriptTool工具组件页面
                        renderScriptToolPage(node.data.dispatcher_detail_id);
                    }else if(data.tool_type === 'VARIABLE_DEFINITION'){
                        // 渲染VariableDefinitionTool工具组件页面
                        renderVariableDefinitionToolPage(node.data.dispatcher_detail_id, data.variables);
                    }else if(data.tool_type === 'HTTP_HEADER_MANAGER'){
                        // 渲染HTTPHeaderManagerTool工具组件页面
                        renderHTTPHeaderManagerToolPage(node.data.dispatcher_detail_id, data.variables);
                    }else if(data.tool_type === 'HTTP_COOKIE_MANAGER'){
                        // 渲染HTTPCookieManagerTool工具组件页面
                        renderHTTPCookieManagerToolPage(node.data.dispatcher_detail_id, data.attributes);
                    }
                    resolve(['成功了', 'success']);  // 也可以传递其他类型参数
                }else{
                    // 渲染失败从已渲染数组中删除
                    rendered_node_arr.splice($.inArray(node.data.dispatcher_detail_id, rendered_node_arr), 1);
                    message("工具信息查询失败: " + data.error_msg, "error");
                }
            },
        })
    });
}

// 渲染案例页面组件
function renderCasePage(dispatcher_detail_id, data_expectations) {
    // dispatcher_detail_id: 当前页面所属的元素调度id编号，作为区分不通元素页面的唯一标识
    // data_expectations: 案例执行的期望数据

    // 元素
    let $radio_default_request_code = $('#radio-default-request-code-' + dispatcher_detail_id);
    let $radio_beautify_request_code = $('#radio-beautify-request-code-' + dispatcher_detail_id);
    let $radio_default_response_code = $('#radio-default-response-code-' + dispatcher_detail_id);
    let $radio_beautify_response_code = $('#radio-beautify-response-code-' + dispatcher_detail_id);
    let $div_table_expectation_spinner = $('#div-table-expectation-spinner-' + dispatcher_detail_id);
    let $nav_link_case_expectation_tab = $(`#nav-link-case-expectation-${dispatcher_detail_id}`);

    // 请求头
    let ace_request_header_editor = ace.edit('ace-request-header-' + dispatcher_detail_id);
    ace_request_header_editor.setReadOnly(true);
    ace_request_header_editor.setTheme("ace/theme/eclipse");
    ace_request_header_editor.session.setMode("ace/mode/json");
    let request_header_code = ace_request_header_editor.session.getValue();
    request_header_default_data_arr[dispatcher_detail_id] = request_header_code;
    ace_request_header_editor.session.setValue(js_beautify(request_header_code));
    // 请求体
    let ace_request_body_editor = ace.edit('ace-request-body-' + dispatcher_detail_id);
    ace_request_body_editor.setReadOnly(true);
    ace_request_body_editor.setTheme("ace/theme/eclipse");
    ace_request_body_editor.session.setMode("ace/mode/json");
    let request_body_code = ace_request_body_editor.session.getValue();
    request_body_default_data_arr[dispatcher_detail_id] = request_body_code;
    ace_request_body_editor.session.setValue(js_beautify(request_body_code));
    // 应答头
    let ace_response_header_editor = ace.edit('ace-response-header-' + dispatcher_detail_id);
    ace_response_header_editor.setReadOnly(true);
    ace_response_header_editor.setTheme("ace/theme/eclipse");
    ace_response_header_editor.session.setMode("ace/mode/json");
    let response_header_code = ace_response_header_editor.session.getValue();
    response_header_default_data_arr[dispatcher_detail_id] = response_header_code;
    ace_response_header_editor.setValue(js_beautify(response_header_code));
    // 应答体
    let ace_response_body_editor = ace.edit('ace-response-body-' + dispatcher_detail_id);
    ace_response_body_editor.setReadOnly(true);
    ace_response_body_editor.setTheme("ace/theme/eclipse");
    let response_body_code = ace_response_body_editor.session.getValue();
    response_body_default_data_arr[dispatcher_detail_id] = response_body_code;
    if (isHtml(response_body_code)){
        ace_response_body_editor.session.setMode("ace/mode/html");
        ace_response_body_editor.session.setValue(html_beautify(response_body_code));
    }else{
        ace_response_body_editor.session.setMode("ace/mode/json");
        ace_response_body_editor.session.setValue(js_beautify(response_body_code));
    }
    // 预处理脚本
    let ace_preprocessor_script_editor = ace.edit(`ace-preprocessor-script-${dispatcher_detail_id}`);
    ace_preprocessor_script_editor.setReadOnly(true);
    ace_preprocessor_script_editor.setTheme("ace/theme/eclipse");
    ace_preprocessor_script_editor.session.setMode("ace/mode/python");
    // 后处理脚本
    let ace_postprocessor_script_editor = ace.edit(`ace-postprocessor-script-${dispatcher_detail_id}`);
    ace_postprocessor_script_editor.setReadOnly(true);
    ace_postprocessor_script_editor.setTheme("ace/theme/eclipse");
    ace_postprocessor_script_editor.session.setMode("ace/mode/python");
    // 后处理脚本信息
    let ace_post_processor_script_info_editor = ace.edit('ace-post-processor-script-info-' + dispatcher_detail_id);
    ace_post_processor_script_info_editor.setReadOnly(true);
    ace_post_processor_script_info_editor.setTheme("ace/theme/eclipse");

    // 日志
    let ace_case_log_editor = ace.edit('ace-case-log-' + dispatcher_detail_id);
    ace_case_log_editor.setReadOnly(true);
    ace_case_log_editor.setTheme("ace/theme/log");
    ace_case_log_editor.session.setMode("ace/mode/log");

    // 期望表格渲染
    renderExpectationTable(dispatcher_detail_id, data_expectations);

    // 事件绑定
    $radio_default_request_code.bind("click", defaultRequestCode);
    $radio_beautify_request_code.bind("click", beautifyRequestCode);
    $radio_default_response_code.bind("click", defaultResponseCode);
    $radio_beautify_response_code.bind("click", beautifyResponseCode);
    $nav_link_case_expectation_tab.on('shown.bs.tab', clickCaseExpectationTab);  // 点击案例期望tab页

    // 请求响应文本美化/默认
    function beautifyRequestCode(){
        let request_header_code = ace_request_header_editor.session.getValue();
        let request_body_code = ace_request_body_editor.session.getValue();
        ace_request_header_editor.session.setValue(js_beautify(request_header_code));
        ace_request_body_editor.session.setValue(js_beautify(request_body_code));
    }
    function defaultRequestCode() {
        ace_request_header_editor.session.setValue(request_header_default_data_arr[dispatcher_detail_id]);
        ace_request_body_editor.session.setValue(request_body_default_data_arr[dispatcher_detail_id]);
    }
    function beautifyResponseCode(){
        let response_header_code = ace_response_header_editor.session.getValue();
        let response_body_code = ace_response_body_editor.session.getValue();
        ace_response_header_editor.session.setValue(js_beautify(response_header_code));
        if (isHtml(response_body_code)){
            ace_response_body_editor.session.setMode("ace/mode/html");
            ace_response_body_editor.session.setValue(html_beautify(response_body_code));
        }else{
            ace_response_body_editor.session.setMode("ace/mode/json");
            ace_response_body_editor.session.setValue(js_beautify(response_body_code));
        }
    }
    function defaultResponseCode() {
        ace_response_header_editor.session.setValue(response_header_default_data_arr[dispatcher_detail_id]);
        ace_response_body_editor.session.setValue(response_body_default_data_arr[dispatcher_detail_id]);
    }
    // 点击案例期望tab页
    function clickCaseExpectationTab() {
        let dispatcher_detail_id = $(this).data('dispatcher-detail-id');
        // invisible状态下ht不可被渲染，因此在这里渲染
        $(`#table-expectation-${dispatcher_detail_id}`).handsontable('getInstance').render();
    }

    // 渲染期望表格
    function renderExpectationTable(dispatcher_detail_id, data_expectations) {
        // const container_table_expectation = document.getElementById('table-expectation-' + dispatcher_detail_id);
        const option_table_expectation = {
            data: data_expectations,
            rowHeaders: true,
            // colHeaders: true,
            licenseKey: 'non-commercial-and-evaluation',
            // 表格宽度
            // width: '95vw',  // 不指定宽度使其自适应容器
            // 拉伸方式
            stretchH: 'all',
            // tab键自动换行切换
            autoWrapRow: true,
            height: '40vh',
            // 最大行数
            maxRows: 99,
            // 允许手工移动行或列
            manualRowResize: true,
            manualColumnResize: true,
            // 列名
            colHeaders: [
                '测试字段',
                '匹配模式',
                '期望值',
                '取反',
                '执行结果',
                '错误信息',
            ],
            // 为列设置默认值
            dataSchema: {
                test_field: '响应文本',
                matching_rule: '包括',
                value: '',
                negater: false,
                last_result: false,
                last_failure_msg: '初次执行前默认失败',
            },
            // 设置列数据类型
            columns: [
                {
                    data: 'test_field',
                    type: 'dropdown',
                    readOnly: true,
                    source: ['响应文本', '响应码', '响应头', '请求数据', '请求头']
                },
                {
                    data: 'matching_rule',
                    type: 'dropdown',
                    readOnly: true,
                    source: ['包括', '匹配', '相等', '子字符串', 'XPath']
                },
                {
                    data: 'value',
                    readOnly: true,
                    renderer: renderValue,
                },
                {
                    data: 'negater',
                    type: 'checkbox',
                    readOnly: true,
                },
                {
                    data: 'result',
                    readOnly: true,
                    renderer: renderLastResult,
                },
                {
                    data: 'failure_msg',
                    readOnly: true,
                    // renderer: renderLastFailureMsg, // TODO 处理html字符串导致页面展示异常
                },
            ],
            // 列宽比例
            colWidths: [1.5, 1.5, 3, 1, 1.5, 5],
            // 允许手工移动行或列
            manualRowMove: false,
            manualColumnMove: false,
            // 右键菜单
            // contextMenu: ['row_above', 'row_below', '---------', 'remove_row', '---------', 'undo', 'redo', '---------', 'alignment', '---------', 'copy', 'cut'],
            // 列是否支持过滤
            filters: false,
            // 下拉菜单
            // dropdownMenu: ['make_read_only', '---------', 'alignment'],
            // 语言
            language: 'zh-CN',
            // 是否允许无效数据 默认 true
            allowInvalid: false,
            // afterPaste: afterPasteHookForTableExpectation,
            // beforeChange: beforeChangeHookForTableExpectation,
            afterLoadData: afterLoadDataHookForTableExpectation,
        };

        // let table_expectation = new Handsontable(container_table_expectation, option_table_expectation);
        // 使用jqueryDom来创建ht实例，这样后面可以继续使用jqueryDom来获取ht实例，而不需要将ht实例存储到变量中
        let $container_table_expectation = $(`#table-expectation-${dispatcher_detail_id}`);
        let table_expectation = $container_table_expectation.handsontable(option_table_expectation).handsontable('getInstance');

        function renderLastResult(instance, td, row, col, prop, value, cellProperties) {
            let lastResult = value.toString();  // value is bool
            while (td.firstChild) {
                td.removeChild(td.firstChild);
            }
            if (['true', 'false'].indexOf(lastResult) > -1) {
                let lastResultElement = document.createElement('DIV');
                lastResultElement.className = 'lastResult ' + lastResult.toLowerCase();
                td.appendChild(lastResultElement);
            } else {
                let textNode = document.createTextNode(value === null ? '' : value);
                td.appendChild(textNode);
            }
        }

        function renderValue(instance, td, row, col, prop, value, cellProperties) {
            let xpathSeparator = "|xpathSeparator|";
            let matchingRule = instance.getDataAtCell(row, 1);
            while (td.firstChild) {
                td.removeChild(td.firstChild);  // TODO 会发生异常
            }
            if (matchingRule === "XPath"){
                td.innerHTML = '<div class="input-group input-group-sm mt-1 mb-1">\n' +
                '  <input type="text" class="form-control input-xpath h-75" placeholder="XPath表达式">\n' +
                '  <input type="text" class="form-control input-expectation h-75" placeholder="期望值">\n' +
                '</div>';

                let xpathValue = value.split(xpathSeparator)[0];
                let expectationValue = value.split(xpathSeparator)[1];

                $($(td).find(".input-xpath")[0]).val(xpathValue);
                $($(td).find(".input-expectation")[0]).val(expectationValue);


                function updateValue(){
                    let xpathValue = $($(td).find(".input-xpath")[0]).val();
                    let expectationValue = $($(td).find(".input-expectation")[0]).val();
                    instance.setDataAtCell(row, col, xpathValue + xpathSeparator + expectationValue)  // 会触发表格渲染  或许用loadData？
                }

                function onblurEvent(){
                    updateValue();
                }

                function keydownEvent(e){
                    if (e.keyCode === 13) {  // 回车键
                        updateValue();
                    }
                }

                // $($(td).find(".input-xpath")[0]).bind("input", updateValue);  // 每次输入触发input事件后表格都会渲染一次，光标需要重新选择
                // $($(td).find(".input-expectation")[0]).bind("input", updateValue);
                $($(td).find(".input-xpath")[0]).on("keydown", keydownEvent);
                // $($(td).find(".input-xpath")[0]).blur(onblurEvent);
                $($(td).find(".input-xpath")[0]).on("blur", onblurEvent);
                $($(td).find(".input-expectation")[0]).on("keydown", keydownEvent);
                // $($(td).find(".input-expectation")[0]).blur(onblurEvent);
                $($(td).find(".input-expectation")[0]).on("blur", onblurEvent);
            }else {
                let textNode = document.createTextNode(value === null ? '' : value);
                td.appendChild(textNode);
            }
        }

        // // 解决复制粘贴后checkBox中的值由布尔类型变为字符串的问题
        // function afterPasteHookForTableExpectation() {
        //     let data = table_expectation.getSourceData();
        //     for (let i = 0; i < data.length; ++i) {
        //         let row = data[i];
        //         if (row.negater === 'false'){
        //             row.negater = false;
        //         }
        //         if (row.negater === 'true'){
        //             row.negater = true;
        //         }
        //     }
        //     table_expectation.loadData(data);
        // }
        //
        // function beforeChangeHookForTableExpectation(changes, source){
        //     // 暂时不知道为什么在afterPaste事件处理函数中无法将negater字段重置为布尔类型
        //     // 因此又使用了beforeChange事件处理函数，在negater字段更新前判断要更新的值如果是字符串则重置为布尔类型
        //     if (source === 'CopyPaste.paste'){
        //         for (let i = 0; i < changes.length; i++) {  // changes 更新的数据
        //             let change = changes[i];  // change change[1]:字段名 change[2]:更新前的值(当前的值) change[3]:更新后的值
        //             if (change['1'] === 'negater'){
        //                 if (change['3'] === 'false'){
        //                     change['3'] = false;
        //                 }
        //                 if (change['3'] === 'true'){
        //                     change['3'] = true;
        //                 }
        //             }
        //         }
        //     }
        // }

        // 数据加载完毕后隐藏spinner
        function afterLoadDataHookForTableExpectation() {
           $div_table_expectation_spinner.css('display', 'none');
        }
    }
}
// 渲染ScriptTool工具组件
function renderScriptToolPage(dispatcher_detail_id) {
    // 脚本
    let ace_tool_script_editor = ace.edit(`div-ace-tool-script-${dispatcher_detail_id}`);
    ace_tool_script_editor.setReadOnly(true);
    ace_tool_script_editor.setTheme("ace/theme/eclipse");
    ace_tool_script_editor.session.setMode("ace/mode/python");
}
// 渲染VariableDefinitionTool工具组件页面
function renderVariableDefinitionToolPage(dispatcher_detail_id, data_variables) {
    // 对象
    let table_variable_definition = null;  // 变量定义表格对象

    // 渲染变量定义表格
    renderVariableDefinitionTable();

    // 渲染变量定义表格
    function renderVariableDefinitionTable() {
        const option_table_variable_definition = {
            data: data_variables,
            rowHeaders: true,
            // colHeaders: true,
            licenseKey: 'non-commercial-and-evaluation',
            // 表格宽度
            // width: '95vw',  // 不指定宽度使其自适应容器
            // 拉伸方式
            stretchH: 'all',
            // tab键自动换行切换
            autoWrapRow: true,
            height: '55vh',
            // 最大行数
            maxRows: 999,
            // 允许手工移动行或列
            manualRowResize: true,
            manualColumnResize: true,
            // 列名
            colHeaders: [
                '名称',
                '值',
                '描述',
            ],
            // 为列设置默认值
            dataSchema: {
                name: '',
                value: '',
                url_encode: false,
                content_type: 'text/plain',
                include_equals: true,
            },
            // 设置列数据类型
            columns: [
                {
                    data: 'name',
                    readOnly: true,
                },
                {
                    data: 'value',
                    readOnly: true,
                },
                {
                    data: 'description',
                    readOnly: true,
                },
            ],
            // 列宽比例
            colWidths: [1, 1, 1],
            manualRowMove: true,
            manualColumnMove: false,
            // 右键菜单
            // contextMenu: ['row_above', 'row_below', '---------', 'remove_row', '---------', 'undo', 'redo', '---------', 'alignment', '---------', 'copy', 'cut'],
            // 列是否支持过滤
            filters: false,
            // 下拉菜单
            // dropdownMenu: ['make_read_only', '---------', 'alignment'],
            // 语言
            language: 'zh-CN',
            // 是否允许无效数据 默认 true
            allowInvalid: false,
        };
        let $container = $(`#table-variable-definition-${dispatcher_detail_id}`);
        table_variable_definition = $container.handsontable(option_table_variable_definition).handsontable('getInstance');
    }
}
// 渲染HTTPHeaderManagerTool工具组件页面
function renderHTTPHeaderManagerToolPage(dispatcher_detail_id, data_variables) {
    // HTTP请求头表格对象
    let table_http_header_manager = null;

    // 渲染HTTP请求头表格
    renderHTTPHeaderManagerTable();

    // 渲染HTTP请求头表格
    function renderHTTPHeaderManagerTable() {
        const option_table_http_header_manager = {
            data: data_variables,
            rowHeaders: true,
            // colHeaders: true,
            licenseKey: 'non-commercial-and-evaluation',
            // 表格宽度
            // width: '95vw',  // 不指定宽度使其自适应容器
            // 拉伸方式
            stretchH: 'all',
            // tab键自动换行切换
            autoWrapRow: true,
            height: '55vh',
            // 最大行数
            maxRows: 999,
            // 允许手工移动行或列
            manualRowResize: true,
            manualColumnResize: true,
            // 列名
            colHeaders: [
                '名称',
                '值',
                '描述',
            ],
            // 为列设置默认值
            dataSchema: {
                name: '',
                value: '',
                url_encode: false,
                content_type: 'text/plain',
                include_equals: true,
            },
            // 设置列数据类型
            columns: [
                {
                    data: 'name',
                    readOnly: true,
                },
                {
                    data: 'value',
                    readOnly: true,
                },
                {
                    data: 'description',
                    readOnly: true,
                },
            ],
            // 列宽比例
            colWidths: [1, 1, 1],
            manualRowMove: true,
            manualColumnMove: false,
            // 右键菜单
            // contextMenu: ['row_above', 'row_below', '---------', 'remove_row', '---------', 'undo', 'redo', '---------', 'alignment', '---------', 'copy', 'cut'],
            // 列是否支持过滤
            filters: false,
            // 下拉菜单
            // dropdownMenu: ['make_read_only', '---------', 'alignment'],
            // 语言
            language: 'zh-CN',
            // 是否允许无效数据 默认 true
            allowInvalid: false,
        };
        let $container = $(`#table-http-header-manager-${dispatcher_detail_id}`);
        table_http_header_manager = $container.handsontable(option_table_http_header_manager).handsontable('getInstance');
    }
}
// 渲染HTTPCookieManagerTool工具组件页面
function renderHTTPCookieManagerToolPage(dispatcher_detail_id, data_attributes) {
    // HTTPCookieManager表格对象
    let table_http_cookie_manager = null;

    // 渲染HTTPCookieManager表格
    renderHTTPCookieManagerTable();

    // 渲染HTTPCookieManager表格
    function renderHTTPCookieManagerTable() {
        const option_table_http_header_manager = {
            data: data_attributes,
            rowHeaders: true,
            // colHeaders: true,
            licenseKey: 'non-commercial-and-evaluation',
            // 表格宽度
            // width: '95vw',  // 不指定宽度使其自适应容器
            // 拉伸方式
            stretchH: 'all',
            // tab键自动换行切换
            autoWrapRow: true,
            height: '55vh',
            // 最大行数
            maxRows: 999,
            // 允许手工移动行或列
            manualRowResize: true,
            manualColumnResize: true,
            // 列名
            colHeaders: [
                '名称',
                '值',
                '域',
                '路径',
                '安全',
            ],
            // 为列设置默认值
            dataSchema: {
                name: '',
                value: '',
                url_encode: false,
                content_type: 'text/plain',
                include_equals: true,
            },
            // 设置列数据类型
            columns: [
                {
                    data: 'name',
                    readOnly: true,
                },
                {
                    data: 'value',
                    readOnly: true,
                },
                {
                    data: 'domain',
                    readOnly: true,
                },
                {
                    data: 'path',
                    readOnly: true,
                },
                {
                    data: 'secure',
                    type: 'checkbox',
                    readOnly: true,
                },
            ],
            // 列宽比例
            colWidths: [2, 5, 5, 3, 1],
            manualRowMove: true,
            manualColumnMove: false,
            // 右键菜单
            // contextMenu: ['row_above', 'row_below', '---------', 'remove_row', '---------', 'undo', 'redo', '---------', 'alignment', '---------', 'copy', 'cut'],
            // 列是否支持过滤
            filters: false,
            // 下拉菜单
            // dropdownMenu: ['make_read_only', '---------', 'alignment'],
            // 语言
            language: 'zh-CN',
            // 是否允许无效数据 默认 true
            allowInvalid: false,
        };
        let $container = $(`#table-http-cookie-manager-${dispatcher_detail_id}`);
        table_http_cookie_manager = $container.handsontable(option_table_http_header_manager).handsontable('getInstance');
    }
}

// 渲染报告图表
function renderEcharts() {
    // 基于准备好的dom，初始化echarts实例
    let echartPieResult = echarts.init(document.getElementById('echart-pie-result'));

    // 指定图表的配置项和数据
    let option = {
        title: {
            text: '',
            subtext: '',
            left: 'right'
        },
        tooltip: {
            trigger: 'item',
            formatter: '{a} <br/>{b} : {c} ({d}%)'
        },
        legend: {
            orient: 'vertical',
            left: 'left',
            data: ['成功', '失败', '错误', '跳过']
        },
        color: ['#28a645','#da3545', '#cd00cc', '#6b747d'],
        series: [
            {
                name: '执行结果',
                type: 'pie',
                radius: ['50%', '75%'],
                selectedMode: 'single',
                center: ['50%', '50%'],
                label: {
                    show: false,
                    position: 'center',
                    alignTo: 'labelLine',
                    bleedMargin: 5,
                },
                data: echart_pie_result_data,
                emphasis: {
                    itemStyle: {
                        shadowBlur: 10,
                        shadowOffsetX: 0,
                        shadowColor: 'rgba(0, 0, 0, 0.5)'
                    },
                    label: {
                        show: true,
                        fontSize: '15',
                        fontWeight: 'bold'
                    }
                },
                labelLine: {
                    show: false
                },
            }
        ]
    };

    // 使用刚指定的配置项和数据显示图表。
    echartPieResult.setOption(option);

    // 事件注册
    echartPieResult.on('click', function (param) {
        if(param.componentSubType === 'pie' && param.componentType === 'series'){
            if(param.name === '失败'){
                filterCase('失败');
            }else if(param.name === '成功'){
                filterCase('成功');
            }else if(param.name === '错误'){
                filterCase('错误');
            }else if(param.name === '跳过'){
                filterCase('跳过');
            }
        }
    });

    // 定时更新直到调度结束
    let _iIntervalID = setInterval(function() {
        $.ajax({
            type: 'POST',
            url: '/ajax/report_detail/case_result_count/get',
            data: {
                dispatcher_id: dispatcher_id,
            },
            success: function (data, status, xhr) {
                if (data.error_no === 0){
                    if(data.status === '已完成' || data.status === '已停止'){
                        clearInterval(_iIntervalID);
                    }
                    let success_count = 0;
                    let failure_count = 0;
                    let error_count = 0;
                    let skip_count = 0;
                    $.each(data.case_result_count_data, function (index, count_data) {
                        if (count_data.name === '成功'){
                            success_count = count_data.value;
                        }else if(count_data.name === '失败'){
                            failure_count = count_data.value;
                        }else if(count_data.name === '错误'){
                            error_count = count_data.value;
                        }else if(count_data.name === '跳过'){
                            skip_count = count_data.value;
                        }
                    });
                    let total_count = success_count + failure_count + error_count + skip_count;
                    $report_case_total_count.text(total_count);
                    $report_case_success_count.text(success_count);
                    $report_case_failure_count.text(failure_count);
                    $report_case_error_count.text(error_count);
                    $report_case_skip_count.text(skip_count);
                    let option = echartPieResult.getOption();
                    option.series[0].data = data.case_result_count_data;
                    echartPieResult.setOption(option);  // 更新echarts数据
                }else{
                    message("案例执行数据查询失败: " + data.error_msg, "error");
                }
            },
        });
    }, 3000);
}

// 渲染报告节点树
function renderReportTree() {
    $("#report-tree").fancytree({
        // 如果是false则页面最初加载时，未展开的节点将不会生成dom元素，如果是true则会生成dom元素。
        activeVisible: true, // Make sure, active nodes are visible (expanded)
        // 无障碍属性
        aria: true, // Enable WAI-ARIA support
        // 使用键盘切换时会自动activate一个node
        autoActivate: true, // Automatically activate a node when it is focused using keyboard
        // 只允许有一个节点展开
        autoCollapse: false, // Automatically collapse all siblings, when a node is expanded
        // 自动滚动到可见区域
        autoScroll: true, // Automatically scroll nodes into visible area
        click: function (event, data){
            // 隐藏其他组件
            $('.report-detail-data').css('display', 'none');
            let node = data.node;
            let element_type = node.data.element_type;
            let dispatcher_detail_id = node.data.dispatcher_detail_id;
            $div_report_detail_data_load_spinner.css('display', '');
            setTimeout(function() {
                // 找到需要展示的场景Dom
                $.wait(
                    `#dispatcher-detail-${dispatcher_detail_id}`,
                    function ($element) {
                        $('.report-detail-data').css('display', 'none');  // 防止在等待过程中其他组件展示
                        $div_report_detail_data_load_spinner.css("cssText", "display: none !important;");
                        $element.css('display', '');
                        if(element_type === 'TOOL' && node.data.tool_type === 'VARIABLE_DEFINITION'){
                            $(`#table-variable-definition-${dispatcher_detail_id}`).handsontable('getInstance').render();
                        }else if(element_type === 'TOOL' && node.data.tool_type === 'HTTP_HEADER_MANAGER'){
                            $(`#table-http-header-manager-${dispatcher_detail_id}`).handsontable('getInstance').render();
                        }else if(element_type === 'TOOL' && node.data.tool_type === 'HTTP_COOKIE_MANAGER'){
                            $(`#table-http-cookie-manager-${dispatcher_detail_id}`).handsontable('getInstance').render();
                        }
                    },
                    1000,
                    500,
                );
            }, 100);
        },
        // 点击folder node时触发的行为
        clickFolderMode: 4, // 1:activate, 2:expand, 3:activate and expand, 4:activate (dblclick expands)
        // 展示复选框
        checkbox: false, // Show check boxes
        // 复选框自动隐藏
        checkboxAutoHide: undefined, // Display check boxes on hover only
        debugLevel: 2, // 0:quiet, 1:errors, 2:warnings, 3:infos, 4:debug
        disabled: false, // Disable control
        extensions: ["filter"],  // 启动拓展插件
        escapeTitles: false, // Escape `node.title` content for display
        focusOnSelect: false, // Set focus when node is checked by a mouse click
        filter: {  // override default settings
            autoExpand: true,  // 匹配到节点时展开
            counter: true, // 在父节点上展示匹配到子节点的数量
            mode: "hide"  // "dimm": Grayout unmatched nodes, "hide": remove unmatched nodes
        },
        // 是否生成id
        generateIds: true, // Generate id attributes like <span id='fancytree-id-KEY'>
        // id前缀
        idPrefix: "ft_", // Used to generate node idÂ´s like <span id='fancytree-id-<key>'>
        // 是否展示图标  Display node icons
        icon: function (event, data) {
            let node = data.node;
            let element_type = node.data.element_type;
            let case_type = node.data.case_type;
            let case_result = node.data.case_result;
            let logic_controller_type = node.data.logic_controller_type;
            let tool_type = node.data.tool_type;
            let resultSuffix = '';
            if(case_result === '成功'){
                resultSuffix = '-success';
            }else if(case_result === '失败'){
                resultSuffix = '-failure';
            }else if(case_result === '错误'){
                resultSuffix = '-error';
            }else if(case_result === '跳过'){
                resultSuffix = '-skip';
            }
            if (element_type === 'PROJECT') {
                return 'icon-project';
            }else if(element_type === 'MODULE') {
                return 'icon-module';
            }else if(element_type === 'SCENE') {
                return 'icon-scene';
            }else if(element_type === 'CASE'){
                if(case_type === 'HTTP'){
                    return 'icon-case-http' + resultSuffix;
                }else if(case_type === 'SQL'){
                    return 'icon-case-sql' + resultSuffix;
                }else if(case_type === 'SSH'){
                    return 'icon-case-ssh' + resultSuffix;
                }else if(case_type === 'DEBUG'){
                    return 'icon-case-debug' + resultSuffix;
                }
                return 'icon-case';
            }else if (element_type === 'LOGIC_CONTROLLER'){
                if(logic_controller_type === 'IF'){
                    return 'icon-if-controller';
                }else if(logic_controller_type === 'SCENE'){
                    return 'icon-scene';
                }else if(logic_controller_type === 'WHILE'){
                    return 'icon-while-controller';
                }else if(logic_controller_type === 'LOOP'){
                    return 'icon-loop-controller';
                }else if(logic_controller_type === 'SIMPLE'){
                    return 'icon-simple-controller';
                }
            }else if (element_type === 'TOOL'){
                if(tool_type === 'TIMER'){
                    return 'icon-tool-timer';
                }else if(tool_type === 'SCRIPT'){
                    return 'icon-tool-script';
                }else if(tool_type === 'VARIABLE_DEFINITION'){
                    return 'icon-tool-variable-definition';
                }else if(tool_type === 'HTTP_HEADER_MANAGER'){
                    return 'icon-tool-http-header-manager';
                }else if(tool_type === 'HTTP_COOKIE_MANAGER'){
                    return 'icon-tool-http-cookie-manager';
                }
            }else if(element_type === undefined) {
                return true;
            }
        },
        // icon的tooltip
        iconTooltip: function(event, data) {
            return data.node.title;
        },
        // The tree widget was initialized, source data was loaded, visible nodes are rendered,
        // selection propagation applied, and node activated.
        init: function (event, data) {
            let promise_array = [];
            reportTree = $.ui.fancytree.getTree("#report-tree");
            reportTree.visit(function (node) {
                // 节点全部展开
                node.setExpanded(true);
                // 只有未被渲染的节点才进行渲染 rendered_node_arr记录已被渲染的节点
                if ($.inArray(node.data.dispatcher_detail_id, rendered_node_arr) === -1){
                    rendered_node_arr.push(node.data.dispatcher_detail_id);
                    // 添加报告详细数据
                    if (node.data.element_type === 'PROJECT'){
                        promise_array.push(renderProjectReportDetail(node));
                    }else if (node.data.element_type === 'MODULE'){
                        promise_array.push(renderModuleReportDetail(node));
                    }else if (node.data.element_type === 'LOGIC_CONTROLLER' && node.data.logic_controller_type === 'SCENE'){
                        promise_array.push(renderSceneReportDetail(node));
                    }else if (node.data.element_type === 'LOGIC_CONTROLLER'){
                        promise_array.push(renderLogicControllerReportDetail(node));
                    }else if (node.data.element_type === 'CASE'){
                        promise_array.push(renderCaseReportDetail(node));
                    }else if (node.data.element_type === 'TOOL'){
                        promise_array.push(renderToolReportDetail(node));
                    }
                }
            });
            // 默认active根节点
            let reportRootNode = reportTree.rootNode.children[0];
            reportRootNode.setActive();

            // 全部加载完后将spinner隐藏
            Promise.all(promise_array).then((result) => {
                // console.log('then');
                // console.log(result);               // [['成功了', 'success'], ...]
                $div_report_detail_data_load_spinner.css("cssText", "display: none !important;");
            }).catch((error) => {
                // console.log('catch');
                // console.log(error);
            }).finally(() => {
                // console.log('finally');
            });
        },
        // 使用键盘导航
        keyboard: true, // Support keyboard navigation
        keyPathSeparator: "/", // Used by node.getKeyPath() and tree.loadKeyPath()
        // root 节点不可折叠
        minExpandLevel: 1, // 1: root node is not collapsible
        // 当tree loader未返回数据时显示一个消息
        nodata: true, // Display a special message when the tree loader returned no data (default: true)
        // 数据后处理
        postProcess: function(event, data){
            let response = data.response;
            if (response.error_no === -1 ){  // 该接口只有错误应答带error_no, 正常数据返回没有error_no
                message("查询报告节点树失败: " + response.error_msg, "error")
            }else{
                data.result = response.tree_data;  // 二次处理应答，将tree_data数据作为结果展示
            }
        },
        // 通过输入首字母快速定位
        quicksearch: true, // Navigate to next node by typing the first letters
        rtl: false, // Enable RTL (right-to-left) mode
        // 初始化数据源
        // source: [
        //     {title: "Node 1", key: "1"},
        //     {title: "Folder 2", key: "2", folder: true, active: true, expanded: true, children: [
        //         {title: "Node 2.1", key: "3", myOwnAttr: "abc", hhh: "这是我定义的"},
        //         {title: "Node 2.2", key: "4"}
        //     ]}
        // ],
        source: {
            type: 'POST',
            url: '/ajax/report_detail/tree',
            data: {
                dispatcher_id: dispatcher_id,
            }
        },
        selectMode: 2, // 1:single, 2:multi, 3:multi-hier
        // tree可以被tab键选中
        tabindex: "0", // Whole tree behaves as one single control
        // 使用tab键切换node
        titlesTabbable: true, // Node titles can receive keyboard focus
        // node节点tooltip
        tooltip: function (event, data) {
            return data.node.title;
        }, // Use title as tooltip (also a callback could be specified)
        // https://wwwendt.de/tech/fancytree/doc/jsdoc/global.html#FancytreeOptions
    });

    // 定时更新直到调度结束
    let _iIntervalID = setInterval(function() {
        $.ajax({
            type: 'POST',
            url: '/ajax/report_detail/tree',
            data: {
                dispatcher_id: dispatcher_id,
            },
            success: function (data, status, xhr) {
                if (data.error_no === 0){
                    if(data.status === '已完成' || data.status === '已停止'){
                        clearInterval(_iIntervalID);  // 页面加载时可能该ajax请求阻塞，而定时器在重复执行，导致多次reload tree，体验不太好
                    }
                    $.ui.fancytree.getTree("#report-tree").reload(data.tree_data);
                }else{
                    message("报告节点树数据查询失败: " + data.error_msg, "error");
                }
            },
        });
    }, 3000);
}

// 报告树过滤案例
function filterCase(result) {
    if(result === filterResultFlag){
        $.ui.fancytree.getTree("#report-tree").clearFilter();
        filterResultFlag = '';
        return;
    }
    $.ui.fancytree.getTree("#report-tree").filterNodes(function(node) {
        let element_type = node.data.element_type;
        let case_result = node.data.case_result;
        if(element_type === 'CASE' && case_result !== result){
            return false;
        }
        filterResultFlag = result;
        return true;
    });
}

// 展示报告日志
function showReportLogModal() {
    $modal_report_log.modal('show');
}
